﻿/*****************************************************************************\
 *             University of Illinois/NCSA Open Source License
 *
 * 
 *      Copyright © 2012,2013, The Board of Trustees of the University of Illinois. All rights reserved.
 *
 *      Developed by: Rakesh Bobba, Derek Dagit, Erich Heine, Denis Kholine,
 *                    Klara Nahrstedt, David Rodgers, William H. Sanders, Tim Yardley
 *
 *      Information Trust Institute/Coordinated Science Laboratory
 * 
 *      University of Illinois
 *      
 *      The Board of Trustees of the University of Illinois
 *      109 Coble Hall
 *      801 South Wright Street
 *      Champaign, IL 61820-6242
 *
 *      http://www.iti.illinois.edu / http://csl.illinois.edu
 * 
 *      Permission is hereby granted, free of charge, to any person obtaining a copy of this software and 
 *      associated documentation files (the “Software”), to deal with the Software without restriction, 
 *      including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
 *      and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so,
 *      subject to the following conditions:
 *
 *      Redistributions of source code must retain the above copyright notice, this list of conditions and the
 *      following disclaimers.
 *      
 *      Redistributions in binary form must reproduce the above copyright notice, this list of conditions and
 *      the following disclaimers in the documentation and/or other materials provided with the distribution.
 *
 *      Neither the names of Information Trust institute, Coordinated Science Laboratory, University of Illinois,
 *      The Board of Trustees of the University of Illinois nor the names of its contributors may be used to 
 *      endorse or promote products derived from this Software without specific prior written permission.
 *      
 *      THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT
 *      LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN
 *      NO EVENT SHALL THE CONTRIBUTORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
 *      WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 
 *      SOFTWARE OR THE USE OR OTHER DEALINGS WITH THE SOFTWARE.
 *      
 \*****************************************************************************/

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading;
using SIEGate.Core.Legacy;
using SIEGate.Core.Measurements;
using SIEGate.Core.Measurements.Values;

namespace SIEGate.Core.Stats
{
    // It would be better to use a delegate for this, but I'm not sure
    // how to pass member functions around instead of static functions or 
    // defined delegates.
    public enum TypeOfStat
    {
        Current,
        LastInterval,
        SinceLastInterval
    }

    public abstract class StatInst : IDuplicableStat
    {
        private readonly string m_name;
        public Func<string> GetOwnerName;

        public abstract dynamic CurrentAggregateValue { get; }
        public abstract dynamic LastIntervalValue { get; }
        public abstract dynamic SinceLastIntervalValue { get; }
        // Reintroducing the concept of Reset since ConcentratorBase has a ResetStatistics.
        // That doesn't mean that what's already happened hasn't happened, but it does mean 
        // that we need to be able to see what the value since last reset was
        public abstract dynamic SinceLastResetValue { get; } 

        public string Name
        {
            get { return m_name;  }
        }

        protected StatInst(Func<string> ownerNameGetFunction, string name)
        {
            GetOwnerName = ownerNameGetFunction;
            m_name = name;
        }

        public abstract dynamic DuplicateCurrentStatus();
        public abstract dynamic GetValueSinceLastResetAndReset();
        internal abstract void IntervalComplete();
        internal abstract StatMapper.StatMeasurementCreator GetMeasurementCreator(Guid streamId, uint statSourceId, TypeOfStat converterType);

        public override string ToString()
        {
            StringBuilder builder = new StringBuilder();
            System.Threading.Interlocked.MemoryBarrier();
            builder.Append("Current aggregate value: ");
            builder.Append(CurrentAggregateValue);
            builder.Append(", last interval value: ");
            builder.Append(LastIntervalValue);
            builder.Append(", since last interval value: ");
            builder.Append(SinceLastIntervalValue);
            return builder.ToString();
        }
    }

    public abstract class SingleStatInst : StatInst
    {
        protected SingleStatInst(Func<string> ownerNameGetFunction, string name) 
            : base(ownerNameGetFunction, name)
        {
        }
    }

    // Problem:  Counters can be integers or floating-point numbers.
    // And ValueType isn't allowed as a generic.
    // So we'll force counters to be integers for now.
    public class Counter : SingleStatInst
    {
        #region [ Measurement Creators ]
        private class CurrentStatMeasurementCreator
            : StatMapper.StatMeasurementCreator
        {
            public CurrentStatMeasurementCreator(Counter stat, Guid streamId, uint statSourceId)
                : base(stat, streamId)
            {
            }

            public override dynamic CreateMeasurement()
            {
                return new Measurement(
                    StreamId,
                    DateTime.Now,
                    MeasurementStateFlags.Normal,
                    new LongValue(((Counter)Stat).CurrentAggregateValue));
            }
        }

        private class LastIntervalStatMeasurementCreator
            : StatMapper.StatMeasurementCreator
        {
            public LastIntervalStatMeasurementCreator(Counter stat, Guid streamId, uint statSourceId)
                : base(stat, streamId)
            {
            }

            public override dynamic CreateMeasurement()
            {
                return new Measurement(
                    StreamId,
                    DateTime.Now,
                    MeasurementStateFlags.Normal,
                    new LongValue(((Counter)Stat).LastIntervalValue));
            }
        }

        private class SinceLastIntervalStatMeasurementCreator
            : StatMapper.StatMeasurementCreator
        {
            public SinceLastIntervalStatMeasurementCreator(Counter stat, Guid streamId, uint statSourceId)
                : base(stat, streamId)
            {
            }

            public override dynamic CreateMeasurement()
            {
                return new Measurement(
                    StreamId,
                    DateTime.Now,
                    MeasurementStateFlags.Normal,
                    new LongValue(((Counter)Stat).SinceLastIntervalValue));
            }
        }
        #endregion

        private long m_sinceStart = 0;
        private long m_valueAtLastIntervalReset = 0;
        /// <summary>
        /// What the difference between CurrentValue and valueAtLastIntervalReset was.
        /// </summary>
        private long m_lastIntervalValue = 0;
        /// <summary>
        /// What m_sinceStart was when the Counter was last reset.
        /// </summary>
        private long m_lastResetValue = 0;

        internal Counter(Func<string> ownerNameGetFunction, string name)
            : base(ownerNameGetFunction, name)
        {

        }

        protected Counter(Func<string> ownerNameGetFunction, string name, long sinceStart, long valueAtLastIntervalReset, long lastIntervalValue)
            : base(ownerNameGetFunction, name)
        {
            m_sinceStart = sinceStart;
            m_valueAtLastIntervalReset = valueAtLastIntervalReset;
            m_lastIntervalValue = lastIntervalValue;
        }

        public void Add(long toAdd)
        {
            System.Threading.Interlocked.Add(ref m_sinceStart, toAdd);
        }

        // TODO: Profile with and without VolatileRead and make sure it's not causing too much performance hurt.
        public override dynamic CurrentAggregateValue
        {
            get { return Thread.VolatileRead(ref m_sinceStart); }
        }

        public override dynamic LastIntervalValue
        {
            get { return Thread.VolatileRead(ref m_lastIntervalValue); }
        }

        public override dynamic SinceLastIntervalValue
        {
            get { return Thread.VolatileRead(ref m_sinceStart) - Thread.VolatileRead(ref m_valueAtLastIntervalReset); }
        }

        public override dynamic SinceLastResetValue
        {
            get
            {
                // We want to guarantee that m_sinceStart >= m_lastResetValue
                long valueAtLastReset = Thread.VolatileRead(ref m_lastResetValue);
                System.Threading.Interlocked.MemoryBarrier();
                long sinceStart = Thread.VolatileRead(ref m_sinceStart);
                return sinceStart - valueAtLastReset;
            }
        }

        public override dynamic DuplicateCurrentStatus()
        {
            return new Counter(GetOwnerName, Name, Thread.VolatileRead(ref m_sinceStart), Thread.VolatileRead(ref m_valueAtLastIntervalReset), Thread.VolatileRead(ref m_lastIntervalValue));
        }

        public override dynamic GetValueSinceLastResetAndReset()
        {
            long toReturn = SinceLastResetValue;
            m_lastResetValue = m_sinceStart;
            return toReturn;
        }

        internal override void IntervalComplete()
        {
            // We want to guarantee that sinceStart >= valueAtLastIntervalReset
            long valueAtLastIntervalReset = Thread.VolatileRead(ref m_valueAtLastIntervalReset);
            System.Threading.Interlocked.MemoryBarrier();
            long sinceStart = Thread.VolatileRead(ref m_sinceStart);
            System.Threading.Interlocked.MemoryBarrier();
            m_lastIntervalValue = sinceStart - valueAtLastIntervalReset;
            m_valueAtLastIntervalReset = sinceStart;
        }

        // HACK: This is kind of lame but we need to have copy-and-paste code here so that the right type of converter is used (Counter, Double, TimeSpan)
        internal override StatMapper.StatMeasurementCreator GetMeasurementCreator(Guid streamId, uint statSourceId, TypeOfStat converterType)
        {
            if (converterType.Equals(TypeOfStat.Current))
            {
                return new CurrentStatMeasurementCreator(this, streamId, statSourceId);
            }
            else if (converterType.Equals(TypeOfStat.LastInterval))
            {
                return new LastIntervalStatMeasurementCreator(this, streamId, statSourceId);
            }
            else
            {
                Debug.Assert(converterType.Equals(TypeOfStat.SinceLastInterval));
                return new SinceLastIntervalStatMeasurementCreator(this, streamId, statSourceId);
            }
        }
    }

    /// <summary>
    /// A counter that uses a delegate to get its statistics instead of stores
    /// those statistics locally.
    /// </summary>
    public class DelegateCounter : Counter
    {
        protected Func<long> GetAggregateValueFunction { get; set; }
        private long m_valueAtLastReset = 0;
        private long m_lastIntervalValue = 0;

        public DelegateCounter(Func<string> ownerNameGetFunction, string name, Func<long> getValueFunction)
            : this(ownerNameGetFunction, name, getValueFunction, 0, 0)
        {
            
        }

        public DelegateCounter(Func<string> ownerNameGetFunction,
                               string name, 
                               Func<long> getValueFunction, 
                               long valueAtLastReset,
                               long lastIntervalValue)
            : base(ownerNameGetFunction, name)
        {
            GetAggregateValueFunction = getValueFunction;
            m_valueAtLastReset = valueAtLastReset;
            m_lastIntervalValue = lastIntervalValue;
        }

        public override dynamic CurrentAggregateValue
        {
            get { return GetAggregateValueFunction(); }
        }

        public override dynamic LastIntervalValue
        {
            get { return m_lastIntervalValue; }
        }

        public override dynamic SinceLastIntervalValue
        {
            get { return GetAggregateValueFunction() - m_valueAtLastReset; }
        }

        public override dynamic DuplicateCurrentStatus()
        {
            // Since DuplicateCurrentStatus is for maintaining state, we need
            // to lock in whatever values are currently stored.
            long currentValue = CurrentAggregateValue;
            return new DelegateCounter(GetOwnerName, Name, () => currentValue, m_valueAtLastReset, m_lastIntervalValue);
        }

        internal override void IntervalComplete()
        {
            long valueAtLastReset = m_valueAtLastReset;
            System.Threading.Interlocked.MemoryBarrier();
            long sinceStart = GetAggregateValueFunction();
            System.Threading.Interlocked.MemoryBarrier();
            m_lastIntervalValue = sinceStart - valueAtLastReset;
            m_valueAtLastReset = sinceStart;
        }
    }

    // DelegateDoubleGenerator exists and is used.  Down the line we may have other ways to generate floats.
    // StatMapper needs to have a way to produce Float measurements, so I'm creating this class
    // so that I can cast to FloatGenerator instead of to DelegateDoubleGenerator there.
    public abstract class DoubleGenerator : SingleStatInst
    {
        #region [ Measurement Creators ]
        private class CurrentStatMeasurementCreator
            : StatMapper.StatMeasurementCreator
        {
            public CurrentStatMeasurementCreator(DoubleGenerator stat, Guid streamId, uint statSourceId)
                : base(stat, streamId)
            {
            }

            public override dynamic CreateMeasurement()
            {
                return new Measurement(
                    StreamId,
                    DateTime.Now,
                    MeasurementStateFlags.Normal,
                    new LongValue(((DoubleGenerator)Stat).CurrentAggregateValue));
            }
        }

        private class LastIntervalStatMeasurementCreator
            : StatMapper.StatMeasurementCreator
        {
            public LastIntervalStatMeasurementCreator(DoubleGenerator stat, Guid streamId, uint statSourceId)
                : base(stat, streamId)
            {
            }

            public override dynamic CreateMeasurement()
            {
                return new Measurement(
                    StreamId,
                    DateTime.Now,
                    MeasurementStateFlags.Normal,
                    new LongValue(((DoubleGenerator)Stat).LastIntervalValue));
            }
        }

        private class SinceLastIntervalStatMeasurementCreator
            : StatMapper.StatMeasurementCreator
        {
            public SinceLastIntervalStatMeasurementCreator(DoubleGenerator stat, Guid streamId, uint statSourceId)
                : base(stat, streamId)
            {
            }

            public override dynamic CreateMeasurement()
            {
                return new Measurement(
                    StreamId,
                    DateTime.Now,
                    MeasurementStateFlags.Normal,
                    new LongValue(((DoubleGenerator)Stat).SinceLastIntervalValue));
            }
        }
        #endregion

        protected DoubleGenerator(Func<string> ownerNameGetFunction, string name)
            : base(ownerNameGetFunction, name)
        {
        }

        // HACK: This is kind of lame but we need to have copy-and-paste code here so that the right type of converter is used (Counter, Double, TimeSpan)
        internal override StatMapper.StatMeasurementCreator GetMeasurementCreator(Guid streamId, uint statSourceId, TypeOfStat converterType)
        {
            if (converterType.Equals(TypeOfStat.Current))
            {
                return new CurrentStatMeasurementCreator(this, streamId, statSourceId);
            }
            else if (converterType.Equals(TypeOfStat.LastInterval))
            {
                return new LastIntervalStatMeasurementCreator(this, streamId, statSourceId);
            }
            else
            {
                Debug.Assert(converterType.Equals(TypeOfStat.SinceLastInterval));
                return new SinceLastIntervalStatMeasurementCreator(this, streamId, statSourceId);
            }
        }
    }

    /// <summary>
    /// A DoubleGenerator that uses a delegate to get its statistics instead of stores
    /// those statistics locally.
    /// </summary>
    public class DelegateDoubleGenerator : DoubleGenerator
    {
        protected Func<double> GetAggregateValueFunction { get; set; }
        private double m_valueAtLastReset = 0;
        private double m_lastIntervalValue = 0;

        public DelegateDoubleGenerator(Func<string> ownerNameGetFunction, string name, Func<double> getValueFunction)
            : this(ownerNameGetFunction, name, getValueFunction, 0, 0)
        {

        }

        public DelegateDoubleGenerator(Func<string> ownerNameGetFunction,
                             string name,
                             Func<double> getValueFunction,
                             double valueAtLastReset,
                             double lastIntervalValue)
            : base(ownerNameGetFunction, name)
        {
            GetAggregateValueFunction = getValueFunction;
            m_valueAtLastReset = valueAtLastReset;
            m_lastIntervalValue = lastIntervalValue;
        }

        public override dynamic CurrentAggregateValue
        {
            get { return GetAggregateValueFunction(); }
        }

        public override dynamic LastIntervalValue
        {
            get { return m_lastIntervalValue; }
        }

        public override dynamic SinceLastIntervalValue
        {
            get { return GetAggregateValueFunction() - m_valueAtLastReset; }
        }

        public override dynamic SinceLastResetValue
        {
            get { throw new NotImplementedException(); }
        }

        public override dynamic DuplicateCurrentStatus()
        {
            // Since DuplicateCurrentStatus is for maintaining state, we need
            // to lock in whatever values are currently stored.
            long currentValue = CurrentAggregateValue;
            return new DelegateDoubleGenerator(GetOwnerName, Name, () => currentValue, m_valueAtLastReset, m_lastIntervalValue);
        }

        public override dynamic GetValueSinceLastResetAndReset()
        {
            throw new NotImplementedException();
        }

        internal override void IntervalComplete()
        {
            double valueAtLastReset = m_valueAtLastReset;
            System.Threading.Interlocked.MemoryBarrier();
            double sinceStart = GetAggregateValueFunction();
            System.Threading.Interlocked.MemoryBarrier();
            m_lastIntervalValue = sinceStart - valueAtLastReset;
            m_valueAtLastReset = sinceStart;
        }
    }

    public class Duration : SingleStatInst
    {
        #region [ Measurement Creators ]
        private class CurrentStatMeasurementCreator
            : StatMapper.StatMeasurementCreator
        {
            public CurrentStatMeasurementCreator(Duration stat, Guid streamId, uint statSourceId)
                : base(stat, streamId)
            {
            }

            public override dynamic CreateMeasurement()
            {
                return new Measurement(
                    StreamId,
                    DateTime.Now,
                    MeasurementStateFlags.Normal,
                    new LongValue(((Duration)Stat).CurrentAggregateValue));
            }
        }

        private class LastIntervalStatMeasurementCreator
            : StatMapper.StatMeasurementCreator
        {
            public LastIntervalStatMeasurementCreator(Duration stat, Guid streamId, uint statSourceId)
                : base(stat, streamId)
            {
            }

            public override dynamic CreateMeasurement()
            {
                return new Measurement(
                    StreamId,
                    DateTime.Now,
                    MeasurementStateFlags.Normal,
                    new LongValue(((Duration)Stat).LastIntervalValue));
            }
        }

        private class SinceLastIntervalStatMeasurementCreator
            : StatMapper.StatMeasurementCreator
        {
            public SinceLastIntervalStatMeasurementCreator(Duration stat, Guid streamId, uint statSourceId)
                : base(stat, streamId)
            {
            }

            public override dynamic CreateMeasurement()
            {
                return new Measurement(
                    StreamId,
                    DateTime.Now,
                    MeasurementStateFlags.Normal,
                    new LongValue(((Duration)Stat).SinceLastIntervalValue));
            }
        }
        #endregion

        private readonly DateTime m_startTime;
        private DateTime m_timeAtLastInterval;
        private DateTime m_timeAtLastReset;
        private TimeSpan m_lastIntervalValue = TimeSpan.Zero;

        internal Duration(Func<string> ownerNameGetFunction, string name)
            : base(ownerNameGetFunction, name)
        {
            // Have to set m_startTime here because it's readonly
            m_startTime = DateTime.Now;
            Initialize(m_startTime, m_startTime, TimeSpan.Zero);
        }

        private Duration(Func<string> ownerNameGetFunction, string name, DateTime startTime, DateTime timeAtLastInterval, DateTime timeAtLastReset, TimeSpan lastIntervalValue)
            : base(ownerNameGetFunction, name)
        {
            // Have to set m_startTime here because it's readonly
            m_startTime = startTime;
            Initialize(timeAtLastInterval, timeAtLastReset, lastIntervalValue);
        }

        // Need this so that call from Duration(Func<string>, string)
        private void Initialize(DateTime timeAtLastInterval, DateTime timeAtLastReset, TimeSpan lastIntervalValue)
        {
            m_timeAtLastReset = timeAtLastReset;
            m_timeAtLastInterval = timeAtLastInterval;
            m_lastIntervalValue = lastIntervalValue;
        }

        public override dynamic CurrentAggregateValue
        {
            get { return DateTime.Now - m_startTime; }
        }

        public override dynamic LastIntervalValue
        {
            get { return m_lastIntervalValue; }
        }

        public override dynamic SinceLastIntervalValue
        {
            get { return DateTime.Now - m_timeAtLastInterval; }
        }

        public override dynamic SinceLastResetValue
        {
            get
            {
                return DateTime.Now - m_timeAtLastReset;
            }
        }

        public override dynamic DuplicateCurrentStatus()
        {
            return new Duration(GetOwnerName, Name, m_startTime, m_timeAtLastInterval, m_timeAtLastReset, m_lastIntervalValue);
        }

        public override dynamic GetValueSinceLastResetAndReset()
        {
            TimeSpan timeSinceLastReset = SinceLastResetValue;
            m_timeAtLastReset = DateTime.Now;
            return timeSinceLastReset;
        }

        internal override void IntervalComplete()
        {
            DateTime timeAtLastReset = m_timeAtLastInterval;
            System.Threading.Interlocked.MemoryBarrier();
            DateTime currentTime = DateTime.Now;
            System.Threading.Interlocked.MemoryBarrier();
            m_lastIntervalValue = currentTime - timeAtLastReset;
            m_timeAtLastInterval = currentTime;
        }

        // HACK: This is kind of lame but we need to have copy-and-paste code here so that the right type of converter is used (Counter, Double, TimeSpan)
        internal override StatMapper.StatMeasurementCreator GetMeasurementCreator(Guid streamId, uint statSourceId, TypeOfStat converterType)
        {
            if (converterType.Equals(TypeOfStat.Current))
            {
                return new CurrentStatMeasurementCreator(this, streamId, statSourceId);
            }
            else if (converterType.Equals(TypeOfStat.LastInterval))
            {
                return new LastIntervalStatMeasurementCreator(this, streamId, statSourceId);
            }
            else
            {
                Debug.Assert(converterType.Equals(TypeOfStat.SinceLastInterval));
                return new SinceLastIntervalStatMeasurementCreator(this, streamId, statSourceId);
            }
        }
    }

    public class CounterTotaler : MultipleStatInst
    {
        #region [ Measurement Creators ]
        private class CurrentStatMeasurementCreator
            : StatMapper.StatMeasurementCreator
        {
            public CurrentStatMeasurementCreator(CounterTotaler stat, Guid streamId, uint statSourceId)
                : base(stat, streamId)
            {
            }

            public override dynamic CreateMeasurement()
            {
                return new Measurement(
                    StreamId,
                    DateTime.Now,
                    MeasurementStateFlags.Normal,
                    new LongValue(((CounterTotaler)Stat).CurrentAggregateValue));
            }
        }

        private class LastIntervalStatMeasurementCreator
            : StatMapper.StatMeasurementCreator
        {
            public LastIntervalStatMeasurementCreator(CounterTotaler stat, Guid streamId, uint statSourceId)
                : base(stat, streamId)
            {
            }

            public override dynamic CreateMeasurement()
            {
                return new Measurement(
                    StreamId,
                    DateTime.Now,
                    MeasurementStateFlags.Normal,
                    new LongValue(((CounterTotaler)Stat).LastIntervalValue));
            }
        }

        private class SinceLastIntervalStatMeasurementCreator
            : StatMapper.StatMeasurementCreator
        {
            public SinceLastIntervalStatMeasurementCreator(CounterTotaler stat, Guid streamId, uint statSourceId)
                : base(stat, streamId)
            {
            }

            public override dynamic CreateMeasurement()
            {
                return new Measurement(
                    StreamId,
                    DateTime.Now,
                    MeasurementStateFlags.Normal,
                    new LongValue(((CounterTotaler)Stat).SinceLastIntervalValue));
            }
        }
        #endregion

        private readonly ISet<Counter> m_counters = new HashSet<Counter>(); 

        public CounterTotaler(Func<string> ownerNameGetFunction, string name)
            : base(ownerNameGetFunction, name)
        {
            _mGetValueInternal = CalculateGetValue;
            _mGetLastIntervalValueInternal = CalculateLastIntervalValue;
            _mGetSinceLastIntervalValue = CalculateSinceLastIntervalValue;
        }

        public CounterTotaler(string ownerName, string statName)
            : this(() => ownerName, statName)
        {
        }

        private CounterTotaler(Func<string> ownerNameGetFunction, string name, Func<dynamic> getValueInternal)
            : base(ownerNameGetFunction, name)
        {
            _mGetValueInternal = CalculateGetValue;
            _mGetLastIntervalValueInternal = CalculateLastIntervalValue;
            _mGetSinceLastIntervalValue = CalculateSinceLastIntervalValue;
        }

        protected long CachedValue { get; set; }
        protected long CachedLastIntervalValue { get; set; }
        protected long CachedSinceLastIntervalValue { get; set; }
        protected long CachedSinceLastResetValue { get; set; }

        private Func<dynamic> _mGetValueInternal;
        private Func<dynamic> _mGetLastIntervalValueInternal;
        private Func<dynamic> _mGetSinceLastResetValue;
        private Func<dynamic> _mGetSinceLastIntervalValue;

        private dynamic m_valueAtLastReset;

        /// <summary>
        /// True when a sum has been calculated.  Does not guarantee that
        /// the cached value is accurate.
        /// </summary>
        private bool _mBCachedValue = false;
        /// <summary>
        /// True when a sum has been calculated for since the last reset.
        /// Does not guarantee that the cached value is accurate.
        /// </summary>
        private bool _mBCachedValueSinceLastInterval = false;
        private bool _mBCachedValueLastInterval = false;
        private bool _mBCachedValueSinceLastReset = false;
        private bool _mBUseCachedValue = false;

        public bool UseCachedValue
        {
            get { return _mBUseCachedValue; }
            set
            {
                if (value != _mBUseCachedValue)
                {
                    _mBUseCachedValue = value;
                    if (value)
                    {
                        _mGetValueInternal = GetCachedValue;
                        _mGetLastIntervalValueInternal = GetCachedLastIntervalValue;
                        _mGetSinceLastIntervalValue = GetCachedSinceLastIntervalValue;
                        _mGetSinceLastResetValue = GetCachedSinceLastResetValue;
                    }
                    else
                    {
                        _mGetValueInternal = CalculateGetValue;
                        _mGetLastIntervalValueInternal = CalculateLastIntervalValue;
                        _mGetSinceLastIntervalValue = CalculateSinceLastIntervalValue;
                        _mGetSinceLastResetValue = CalculateSinceLastResetValue;
                    }
                }
            }
        }
        
        public override dynamic CurrentAggregateValue
        {
            get { return _mGetValueInternal(); }
        }

        public override dynamic LastIntervalValue
        {
            get { return _mGetLastIntervalValueInternal();  }
        }

        public override dynamic SinceLastIntervalValue
        {
            get { return _mGetSinceLastIntervalValue(); }
        }

        public override dynamic SinceLastResetValue
        {
            get { return _mGetSinceLastResetValue(); }
        }

        public override dynamic DuplicateCurrentStatus()
        {
            CounterTotaler toReturn = new CounterTotaler(GetOwnerName, Name, _mGetValueInternal);
            foreach (Counter c in m_counters)
            {
                // We want to freeze whatever values are currently in the
                // counter, so copy them
                toReturn.AddCounter(c.DuplicateCurrentStatus());
            }
            return toReturn;
        }

        public override dynamic GetValueSinceLastResetAndReset()
        {
            dynamic toReturn = SinceLastIntervalValue;
            m_valueAtLastReset = CurrentAggregateValue;
            return toReturn;
        }

        internal override void IntervalComplete()
        {
            _mBCachedValueSinceLastInterval = false;
            _mBCachedValueLastInterval = false;
        }

        // HACK: This is kind of lame but we need to have copy-and-paste code here so that the right type of converter is used (Counter, Double, TimeSpan)
        internal override StatMapper.StatMeasurementCreator GetMeasurementCreator(Guid streamId, uint statSourceId, TypeOfStat converterType)
        {
            if (converterType.Equals(TypeOfStat.Current))
            {
                return new CurrentStatMeasurementCreator(this, streamId, statSourceId);
            }
            else if (converterType.Equals(TypeOfStat.LastInterval))
            {
                return new LastIntervalStatMeasurementCreator(this, streamId, statSourceId);
            }
            else
            {
                Debug.Assert(converterType.Equals(TypeOfStat.SinceLastInterval));
                return new SinceLastIntervalStatMeasurementCreator(this, streamId, statSourceId);
            }
        }

        public void AddCounter(Counter counter)
        {
            m_counters.Add(counter);
        }

        private dynamic CalculateGetValue()
        {
            _mBCachedValue = true;
            return (CachedValue = m_counters.Sum(value => (long) value.CurrentAggregateValue));
        }

        private dynamic GetCachedValue()
        {
            return _mBCachedValue ? CachedValue : CalculateGetValue();
        }

        private dynamic CalculateLastIntervalValue()
        {
            _mBCachedValueLastInterval = true;
            return (CachedLastIntervalValue = m_counters.Sum(value => ((long)value.LastIntervalValue)));
        }

        private dynamic GetCachedLastIntervalValue()
        {
            if (_mBCachedValueLastInterval)
            {
                return CachedLastIntervalValue;
            }
            return CalculateLastIntervalValue();
        }

        private dynamic CalculateSinceLastIntervalValue()
        {
            _mBCachedValueSinceLastInterval = true;
            return (CachedSinceLastIntervalValue = m_counters.Sum(value => ((long) value.SinceLastIntervalValue)));
        }

        private dynamic GetCachedSinceLastIntervalValue()
        {
            if (_mBCachedValueSinceLastInterval)
            {
                return CachedSinceLastIntervalValue;
            }
            return CalculateSinceLastIntervalValue();
        }

        private dynamic CalculateSinceLastResetValue()
        {
            _mBCachedValueSinceLastReset = true;
            return (CachedSinceLastResetValue = m_counters.Sum(value => ((long)value.SinceLastResetValue)));
        }

        private dynamic GetCachedSinceLastResetValue()
        {
            if (_mBCachedValueSinceLastReset)
            {
                return CachedSinceLastResetValue;
            }
            return CalculateSinceLastResetValue();
        }
    }

    public abstract class MultipleStatInst : StatInst
    {
        protected MultipleStatInst(Func<string> ownerNameGetFunction, string name) 
            : base(ownerNameGetFunction, name)
        {
        }
    }
}
